namespace SimpleWebServices.Web.UI.Html
{
    using System;
    using System.Configuration;
    using System.Net;
    using System.Web;
    using System.Xml;
    using System.Xml.XPath;
    using Moq;
    using SimpleWebServices;
    using SimpleWebServices.Configuration;
    using SimpleWebServices.Data;
    using SimpleWebServices.Models;
    using SimpleWebServices.Web.UI.Html.Elements;
    using SimpleWebServices.Web.UI.Html.Validation;
    using Xunit;

    public sealed class FormContainerFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<FormContainer>()
                .DerivesFrom<Resource>()
                .IsAbstractBaseClass()
                .Implements<IContainForm>()
                .XmlRoot("container")
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            string xml =
                "<container" +
                " description='This is a resource.'" +
                " location='/resource'" +
                " title='Resource Title'>" +
                    "<data>" +
                        "<value name='foo'>bar</value>" +
                    "</data>" +
                    "<links>" +
                        "<link rel='canonical' href='http://example.com/resource.en.html' />" +
                    "</links>" +
                    "<metadata>" +
                        "<meta name='DC.date' content='2009-08-01' scheme='W3CDTF' />" +
                    "</metadata>" +
                    "<form action='http://example.com/form'></form>" +
                    "<validators>" +
                        "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices' />" +
                    "</validators>" +
                "</container>";

            FormContainer obj = xml.Deserialize<TestFormContainer>();

            Assert.Equal<string>("bar", obj.Data["foo"]);
            Assert.Equal<string>("This is a resource.", obj.Description);
            Assert.Equal<string>("http://example.com/resource.en.html", obj.Links["canonical"].Hyperlink.AbsoluteUri);
            Assert.Equal<string>("2009-08-01", obj.Metadata["DC.date"].Content);
            Assert.Equal<string>("Resource Title", obj.Title);

            Assert.Equal<string>("http://example.com/form", obj.Form.Action.AbsoluteUri);
            Assert.Equal<Type>(typeof(StandardTextValidator), Type.GetType(obj.ValidatorSettings[0].Type));
        }

        [Fact]
        public void serialize()
        {
            FormContainer obj = new TestFormContainer
            {
                Cacheability = HttpCacheability.NoCache,
                Description = "This is a resource.",
                Etag = "xyz",
                Expires = "P1DT2H3M4.005S",
                Location = "/resource",
                Modified = XmlConvert.ToDateTime("2009-08-01T18:00:00Z", XmlDateTimeSerializationMode.Utc),
                Status = HttpStatusCode.Gone,
                Title = "Resource Title",
                Data =
                {
                    { "foo", "bar" }
                },
                Links =
                {
                    new Link { Relationship = "canonical", Hyperlink = new Uri("http://example.com/resource.en.html") }
                },
                Metadata =
                {
                    new Meta { Name = "DC.date", Content = "1999-12-31", Scheme = "W3CDTF" }
                },
                Form = new Form
                {
                    Action = new Uri("http://example.com/form")
                },
                ValidatorSettings = 
                {
                    new ValidatorSettings
                    {
                        Type = typeof(StandardTextValidator).FullName
                    }
                }
            };

            XPathNavigator navigator = obj.Serialize().CreateNavigator();

            Assert.True((bool)navigator.Evaluate("1=count(/container[@description='This is a resource.'][@title='Resource Title'][@location='/resource'][not(@cacheability)][not(@etag)][not(@expires)][not(@modified)][not(@status)])"));
            Assert.True((bool)navigator.Evaluate("1=count(/container/data/value[@name='foo'][text()='bar'])"));
            Assert.True((bool)navigator.Evaluate("1=count(/container/links/link[@rel='canonical'][@href='http://example.com/resource.en.html'])"));
            Assert.True((bool)navigator.Evaluate("1=count(/container/metadata/meta[@name='DC.date'][@scheme='W3CDTF'][@content='1999-12-31'])"));

            Assert.True((bool)navigator.Evaluate("1=count(/container/form[@action='http://example.com/form'])"));
            Assert.True((bool)navigator.Evaluate("1=count(/container/validators/validator[@type='" + typeof(StandardTextValidator).FullName + "'])"));
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new FormContainerDerived() as FormContainer);
        }

        [Fact]
        public void prop_Form()
        {
            Assert.True(new PropertyExpectations(typeof(FormContainerDerived).GetProperty("Form"))
                .IsAutoProperty<Form>()
                .Result);
        }

        [Fact]
        public void prop_Token()
        {
            Assert.True(new PropertyExpectations(typeof(FormContainerDerived).GetProperty("Token"))
                .IsAutoProperty<Token?>()
                .Result);
        }

        [Fact]
        public void prop_ValidatorSettings()
        {
            Assert.True(new PropertyExpectations(typeof(FormContainerDerived).GetProperty("ValidatorSettings"))
                .TypeIs<ValidatorSettingsCollection>()
                .DefaultValueIsNotNull()
                .Result);
        }

        [Fact]
        public void op_Complete_FormSession()
        {
            Assert.Throws<NotSupportedException>(() => new FormContainerDerived().Complete(null as FormSession));
        }

        [Fact]
        public void op_Render_int_withAbsoluteAction()
        {
            Default.Clear();
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/form?" + token).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FakeRepository.Register<FormSession>(new
                {
                    token = token,
                    xml = "<session><data><value name='control'>test</value></data></session>"
                });

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='http://example.com/form'>" +
                            "<label id='error' for='control'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                XmlDocument html = new XmlDocument();
                html.LoadXml(new TestFormContainer().Populated<TestFormContainer>().Render(1));

                string xpath = "1=count(/form[@action='http://example.com/form?" + token + "'][@method='post']/input[@id='control'][@tabindex='1'][@type='text'][@value='test'])";

                Assert.True((bool)html.CreateNavigator().Evaluate(xpath));

                mock.VerifyAll();
            }
            finally
            {
                HttpContextFactory.Reset();
                FakeRepository.Reset();
                Default.Reset();
            }
        }

        [Fact]
        public void op_Render_int_withRelativeAction()
        {
            Default.Clear();
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/form?" + token).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FakeRepository.Register<FormSession>(new
                {
                    token = token,
                    xml = "<session><data><value name='control'>test</value></data></session>"
                });

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/form'>" +
                            "<label id='error' for='control'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                XmlDocument html = new XmlDocument();
                html.LoadXml(new TestFormContainer().Populated<TestFormContainer>().Render(1));

                string xpath = "1=count(/form[@action='/form?" + token + "'][@method='post']/input[@id='control'][@tabindex='1'][@type='text'][@value='test'])";

                Assert.True((bool)html.CreateNavigator().Evaluate(xpath));

                mock.VerifyAll();
            }
            finally
            {
                HttpContextFactory.Reset();
                FakeRepository.Reset();
                Default.Reset();
            }
        }

        [Fact]
        public void op_Render_int_whenInvalid()
        {
            Default.Clear();
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/form?" + token).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FakeRepository.Register<FormSession>(new
                {
                    token = token,
                    xml = "<session><data><value name='control'></value></data></session>"
                });

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/form'>" +
                            "<label id='error' for='control'/>" +
                            "<input type='text' id='control' name='control' tabindex='9'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                XmlDocument html = new XmlDocument();
                html.LoadXml(new TestFormContainer().Populated<TestFormContainer>().Render(1));

                string xpath = "1=count(/form[@action='/form?" + token + "'][@method='post']/label[@for='control'][@id='error'][@class='error'][text()='The information is not valid.'])";

                Assert.True((bool)html.CreateNavigator().Evaluate(xpath));

                mock.VerifyAll();
            }
            finally
            {
                HttpContextFactory.Reset();
                FakeRepository.Reset();
                Default.Reset();
            }
        }

        [Fact]
        public void op_Render_intZero()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new TestFormContainer().Render(0));
        }

        [Fact]
        public void op_TemplateOfT_derived()
        {
            Assert.Throws<NotSupportedException>(() => new FormContainerDerived().Template<FormContainerDerived>());
        }

        [Fact]
        public void op_TemplateOfT_test()
        {
            try
            {
                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/test'>" +
                            "<label id='error'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                TestFormContainer actual = new TestFormContainer().Template<TestFormContainer>();

                Assert.IsType<ValidatorSettings>(actual.ValidatorSettings[0]);
                Assert.IsType<Label>(actual.Form["error"]);
                Assert.IsType<TextInput>(actual.Form["control"]);
            }
            finally
            {
                Default.Reset();
            }
        }

        [Fact]
        public void op_PopulatedOfT()
        {
            try
            {
                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/form").Verifiable();
                HttpContextFactory.Mock = mock.Object;

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/form'>" +
                            "<label id='error'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                Assert.NotNull(new TestFormContainer().Populated<TestFormContainer>());

                mock.VerifyAll();
            }
            finally
            {
                Default.Reset();
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_PopulatedOfT_withNullTemplate()
        {
            Assert.Throws<ConfigurationErrorsException>(() => new TestFormContainer().Populated<TestFormContainer>());
        }

        [Fact]
        public void op_PopulatedOfT_withToken()
        {
            Default.Clear();
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();
                string expected = "test";

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/form?" + token).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                FakeRepository.Register<FormSession>(new
                {
                    token = token,
                    xml = "<session><data><value name='control'>" + expected + "</value></data></session>"
                });

                Default.Register<TestFormContainer>(
                    "<container>" +
                        "<form action='/form'>" +
                            "<label id='error'/>" +
                            "<input type='text' id='control' name='control'/>" +
                        "</form>" +
                        "<validators>" +
                            "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices'>" +
                                "<data>" +
                                    "<value name='MinLength'>1</value>" +
                                    "<value name='MaxLength'>100</value>" +
                                "</data>" +
                            "</validator>" +
                        "</validators>" +
                    "</container>");

                string actual = (new TestFormContainer().Populated<TestFormContainer>().Form["control"] as TextInput).Value;

                Assert.Equal<string>(expected, actual);

                mock.VerifyAll();
            }
            finally
            {
                HttpContextFactory.Reset();
                FakeRepository.Reset();
                Default.Reset();
            }
        }
    }
}